package com.ruoyi.common;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import com.ruoyi.common.annotation.TableName;
import com.ruoyi.common.annotation.Transient;
import com.ruoyi.common.domain.po.BasePO;
import com.ruoyi.common.domain.po.UserPO;
import com.ruoyi.common.domain.qo.BaseQO;
import com.ruoyi.common.domain.qo.UserQO;
import com.ruoyi.common.enums.BaseInteger;
import com.ruoyi.common.enums.BaseIsDeletedEnum;
import com.ruoyi.common.enums.BaseString;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.handler.BaseIntegerEnumTypeHandler;
import com.ruoyi.common.handler.BaseStringEnumTypeHandler;
import com.ruoyi.common.utils.StringUtils;
import org.apache.ibatis.annotations.Param;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

public class CommonProvider<P extends BasePO,Q extends BaseQO<P>> {

    public String insert(P p) {
        if (p == null) {
            throw new ServiceException("参数不能为空");
        }
        if (p.getGmtCreate() == null) {
            p.setGmtCreate(new Date());
        }
        if (p.getGmtModified() == null) {
            p.setGmtModified(p.getGmtCreate());
        }
        if (p.getIsDeleted() == null) {
            p.setIsDeleted(BaseIsDeletedEnum.EXISTS);
        }
        String tableName = this.getTableNameByPO(p.getClass());
        StringBuilder sql = new StringBuilder("insert into "+tableName + " ");
        List<Field> poFields = Arrays.stream(ReflectUtil.getFields(p.getClass())).collect(Collectors.toList());
//        List<Field> poFields = Arrays.stream(ReflectUtil.getFields(p.getClass())).filter(poField -> ObjectUtil.isNotEmpty(ReflectUtil.getFieldValue(p, poField))).collect(Collectors.toList());
        sql.append("(").append(StringUtils.join(poFields.stream().map(poField -> StringUtils.camelToSnake(poField.getName())).collect(Collectors.toList()), ",")).append(")");
        sql.append(" values (");
        sql.append(StringUtils.join(poFields.stream().map(poField -> {
            if (poField.getType().isEnum()) {
                if (this.isSuperInterface(poField.getType(), BaseInteger.class)) {
                    return "#{" + poField.getName()+ ",typeHandler=" + BaseIntegerEnumTypeHandler.class.getName() + "}";
                } else if (this.isSuperInterface(poField.getType(), BaseString.class)) {
                    return "#{" + poField.getName()+ ",typeHandler=" + BaseStringEnumTypeHandler.class.getName() + "}";
                }
                throw new ServiceException("枚举类型必须继承BaseInteger或者BaseString");
            }else{
                return "#{" + poField.getName() + "}";
            }
        }).collect(Collectors.toList()), ","));
        sql.append(")");
        return sql.toString();
    }

    public String batchInsert(@Param("ps")  List<P> ps) {
        if (CollectionUtils.isEmpty(ps)) {
            throw new ServiceException("参数不能为空");
        }
        Class<? extends BasePO> aClass = ps.get(0).getClass();
        String tableName = this.getTableNameByPO(aClass);
        StringBuilder sql = new StringBuilder("insert into "+tableName + " ");
        List<Field> poFields = Arrays.stream(ReflectUtil.getFields(aClass)).collect(Collectors.toList());
//        List<Field> poFields = Arrays.stream(ReflectUtil.getFields(p.getClass())).filter(poField -> ObjectUtil.isNotEmpty(ReflectUtil.getFieldValue(p, poField))).collect(Collectors.toList());
        sql.append("(").append(StringUtils.join(poFields.stream().map(poField -> StringUtils.camelToSnake(poField.getName())).collect(Collectors.toList()), ",")).append(")");
        sql.append(" values ");
        for (int i = 0; i < ps.size(); i++) {
            AtomicInteger index = new AtomicInteger(i);
            P p = ps.get(i);
            if (p.getGmtCreate() == null) {
                p.setGmtCreate(new Date());
            }
            if (p.getGmtModified() == null) {
                p.setGmtModified(p.getGmtCreate());
            }
            if (p.getIsDeleted() == null) {
                p.setIsDeleted(BaseIsDeletedEnum.EXISTS);
            }
            sql.append("(").append(StringUtils.join(poFields.stream().map(poField -> {
                if (poField.getType().isEnum()) {
                    if (this.isSuperInterface(poField.getType(), BaseInteger.class)) {
                        return "#{ps["+index.get()+"]." + poField.getName()+ ",typeHandler=" + BaseIntegerEnumTypeHandler.class.getName() + "}";
                    } else if (this.isSuperInterface(poField.getType(), BaseString.class)) {
                        return "#{ps["+index.get()+"]."  + poField.getName()+ ",typeHandler=" + BaseStringEnumTypeHandler.class.getName() + "}";
                    }
                    throw new ServiceException("枚举类型必须继承BaseInteger或者BaseString");
                }else{
                    return "#{ps["+index.get()+"]."  + poField.getName() + "}";
                }
            }).collect(Collectors.toList()), ",")).append("),");
        }
        for (P p : ps) {

        }
        return sql.substring(0, sql.length() - 1);
    }

    public String update(Q q) {
        if (q == null) {
            throw new ServiceException("参数不能为空");
        }
        BasePO basePO = q.getUpdatePO();
        if (basePO == null) {
            throw new ServiceException("更新的对象不能为空");
        }
        if (basePO.getGmtModified() == null) {
            basePO.setGmtModified(new Date());
        }
        String tableName = this.getTableNameByQO(q.getClass());
        StringBuilder sql = new StringBuilder("update "+tableName + " set ");
        List<String> excludeFields = new ArrayList<>();
        excludeFields.add("id");
        excludeFields.add("gmtCreate");
        List<Field> poFields = Arrays.stream(ReflectUtil.getFields(basePO.getClass())).filter(poField ->!excludeFields.contains(poField.getName()) ).collect(Collectors.toList());
        sql.append(StringUtils.join(poFields.stream().map(poField -> {
            if (poField.getType().isEnum()) {
                if (this.isSuperInterface(poField.getType(), BaseInteger.class)) {
                    return StringUtils.camelToSnake(poField.getName()) + " = #{updatePO." + poField.getName()+ ",typeHandler=" + BaseIntegerEnumTypeHandler.class.getName() + "} ";
                } else if (this.isSuperInterface(poField.getType(), BaseString.class)) {
                    return StringUtils.camelToSnake(poField.getName()) + " = #{updatePO." + poField.getName()+ ",typeHandler=" + BaseStringEnumTypeHandler.class.getName() + "} ";
                }
                throw new ServiceException("枚举类型必须继承BaseInteger或者BaseString");
            }else{
                return StringUtils.camelToSnake(poField.getName()) + " = #{updatePO." + poField.getName() + "}";
            }
        }).collect(Collectors.toList()), ","));
        List<Field> qoFields = Arrays.stream(ReflectUtil.getFields(q.getClass())).filter(qoField->ObjectUtil.isNotEmpty(ReflectUtil.getFieldValue(q, qoField))).filter(qoField->qoField.getAnnotation(Transient.class)==null).collect(Collectors.toList());
        if (!qoFields.isEmpty()) {
            sql.append(this.getWhere(q, qoFields));
        }
        return sql.toString();
    }
    public boolean isSuperInterface(Class clazz, Class interfaceClass) {
        Class[] interfaces = clazz.getInterfaces();
        for (Class anInterface : interfaces) {
            if (anInterface == interfaceClass) {
                return true;
            }
        }
        return false;

    }

    public static void main(String[] args) {
        UserQO userQO = new UserQO();
        userQO.setName("张三");
        UserPO updatePO = new UserPO();
        updatePO.setName("s");
        userQO.setUpdatePO(updatePO);

        List<UserPO> ls = new ArrayList<>();
        ls.add(updatePO);
        ls.add(updatePO);
        List<String> columns = new ArrayList<>();
        columns.add("id");
        userQO.setId(1L);
        userQO.setIsDeleted(BaseIsDeletedEnum.EXISTS);
//        userQO.setColumns(columns);
        List<String> whereFieldSorts = new ArrayList<>();
        whereFieldSorts.add("id");
        userQO.setWhereFieldSorts(whereFieldSorts);
        System.out.println(new CommonProvider<UserPO, UserQO>().update(userQO));
        System.out.println(new CommonProvider<UserPO, UserQO>().updateSelective(userQO));
        System.out.println(new CommonProvider<UserPO, UserQO>().delete(userQO));
        System.out.println(new CommonProvider<UserPO, UserQO>().select(userQO));
        System.out.println(new CommonProvider<UserPO, UserQO>().insert(updatePO));
        System.out.println(new CommonProvider<UserPO, UserQO>().batchInsert(ls));
    }

    public String updateSelective(Q q) {
        if (q == null) {
            throw new ServiceException("参数不能为空");
        }
        BasePO basePO = q.getUpdatePO();
        if (basePO == null) {
            throw new ServiceException("更新的对象不能为空");
        }
        if (basePO.getGmtModified() == null) {
            basePO.setGmtModified(new Date());
        }
        String tableName = this.getTableNameByQO(q.getClass());
        StringBuilder sql = new StringBuilder("update "+tableName + " set ");
        List<Field> poFields = Arrays.stream(ReflectUtil.getFields(basePO.getClass())).filter(poField -> ObjectUtil.isNotEmpty(ReflectUtil.getFieldValue(basePO, poField))).collect(Collectors.toList());
        //因为占用了修改时间，所以要大于1
        if (poFields.size()==1) {
            throw new ServiceException("更新对象的内容至少要有一个");
        }
        sql.append(StringUtils.join(poFields.stream().map(poField->{
            if (poField.getType().isEnum()) {
                if (this.isSuperInterface(poField.getType(), BaseInteger.class)) {
                    return StringUtils.camelToSnake(poField.getName()) + " = #{updatePO." + poField.getName()+ ",typeHandler=" + BaseIntegerEnumTypeHandler.class.getName() + "} ";
                } else if (this.isSuperInterface(poField.getType(), BaseString.class)) {
                    return StringUtils.camelToSnake(poField.getName()) + " = #{updatePO." + poField.getName()+ ",typeHandler=" + BaseStringEnumTypeHandler.class.getName() + "} ";
                }
                throw new ServiceException("枚举类型必须继承BaseInteger或者BaseString");
            }else{
                return StringUtils.camelToSnake(poField.getName()) + " = #{updatePO." + poField.getName() + "}";
            }
        }).collect(Collectors.toList()), ","));
        List<Field> qoFields = Arrays.stream(ReflectUtil.getFields(q.getClass())).filter(qoField->ObjectUtil.isNotEmpty(ReflectUtil.getFieldValue(q, qoField))).filter(qoField->qoField.getAnnotation(Transient.class)==null).collect(Collectors.toList());
        if (!qoFields.isEmpty()) {
            sql.append(this.getWhere(q, qoFields));
        }
        return sql.toString();
    }
    private String getTableNameByPO(Class<? extends BasePO> clazz) {
        TableName annotation = clazz.getAnnotation(TableName.class);
        if (annotation != null && StringUtils.isNotEmpty(annotation.value())) {
            return annotation.value();
        }
        String  tableName = clazz.getSimpleName();
        tableName = tableName.substring(0, tableName.lastIndexOf("PO"));
        return StringUtils.camelToSnake(tableName);
    }
    private String getTableNameByQO(Class<? extends BaseQO> clazz) {
        TableName annotation = clazz.getAnnotation(TableName.class);
        if (annotation != null && StringUtils.isNotEmpty(annotation.value())) {
            return annotation.value();
        }
        String  tableName = clazz.getSimpleName();
        tableName = tableName.substring(0, tableName.lastIndexOf("QO"));
        return StringUtils.camelToSnake(tableName);
    }
    public String select(Q q) {
        if (q == null) {
            throw new ServiceException("参数不能为空");
        }
        String tableName = this.getTableNameByQO(q.getClass());
        List<Field> qoFields = Arrays.stream(ReflectUtil.getFields(q.getClass())).filter(qoField -> qoField.getAnnotation(Transient.class) == null).filter(qoField -> {
            if (CollectionUtils.isEmpty(q.getColumns())) {
                return true;
            }else{
                if (q.isInclude) {
                    return q.getColumns().contains(qoField.getName()) || q.getColumns().contains(StringUtils.camelToSnake(qoField.getName()));
                } else {
                    return !(q.getColumns().contains(qoField.getName()) || q.getColumns().contains(StringUtils.camelToSnake(qoField.getName())));
                }
            }
        }).collect(Collectors.toList());

        StringBuilder sql = new StringBuilder("select ");
        sql.append(qoFields.stream().map(qoField -> StringUtils.camelToSnake(qoField.getName()) + " as " + qoField.getName()).collect(Collectors.joining(","))).append(" from ").append(tableName).append(" ");
        List<Field> whereFields = qoFields.stream().filter(qoField -> ObjectUtil.isNotEmpty(ReflectUtil.getFieldValue(q, qoField))).collect(Collectors.toList());

        sql.append(this.getWhere(q, whereFields));
        return sql.toString();
    }

    private <P extends BasePO, Q extends BaseQO<P>> String getWhere(Q q, List<Field> whereFields) {
        StringBuilder sql = new StringBuilder("");
        //字段排序
        List<String> whereFieldSorts = q.getWhereFieldSorts();
        if (!CollectionUtils.isEmpty(whereFieldSorts)) {
            whereFieldSorts = whereFieldSorts.stream().filter(whereFieldSort -> ObjectUtil.isNotEmpty(ReflectUtil.getFieldValue(q, whereFieldSort))).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(whereFieldSorts)) {
                whereFields = whereFields.stream().filter(qoField -> !q.getWhereFieldSorts().contains(qoField.getName())).collect(Collectors.toList());
                sql.append(StringUtils.join(whereFieldSorts.stream().map(qoField -> {
                    Field field = ReflectUtil.getField(q.getClass(), qoField);
                    if (field.getType().isEnum()) {
                        if (this.isSuperInterface(field.getType(), BaseInteger.class)) {
                            return StringUtils.camelToSnake(field.getName()) + " = #{" + field.getName()+ ",typeHandler=" + BaseIntegerEnumTypeHandler.class.getName() + "} ";
                        } else if (this.isSuperInterface(field.getType(), BaseString.class)) {
                            return StringUtils.camelToSnake(field.getName()) + " = #{" + field.getName()+ ",typeHandler=" + BaseStringEnumTypeHandler.class.getName() + "} ";
                        }
                        throw new ServiceException("枚举类型必须继承BaseInteger或者BaseString");
                    }else{
                        return StringUtils.camelToSnake(qoField) + " = #{" + qoField + "}";

                    }
                }).collect(Collectors.toList()), " and "));
            }
        }

        if (StringUtils.isNotEmpty(whereFields)) {
            if (!CollectionUtils.isEmpty(whereFieldSorts)) {
                sql.append(" and ");
            }
            sql.append(StringUtils.join(whereFields.stream().map(qoField -> {
                if (qoField.getType().isEnum()) {
                    if (this.isSuperInterface(qoField.getType(), BaseInteger.class)) {
                        return StringUtils.camelToSnake(qoField.getName()) + " = #{" + qoField.getName()+ ",typeHandler=" + BaseIntegerEnumTypeHandler.class.getName() + "} ";
                    } else if (this.isSuperInterface(qoField.getType(), BaseString.class)) {
                        return StringUtils.camelToSnake(qoField.getName()) + " = #{" + qoField.getName()+ ",typeHandler=" + BaseStringEnumTypeHandler.class.getName() + "} ";
                    }
                    throw new ServiceException("枚举类型必须继承BaseInteger或者BaseString");
                }else{
                    return StringUtils.camelToSnake(qoField.getName()) + " = #{" + qoField.getName() + "}";
                }

            }).collect(Collectors.toList()), " and "));
        }
        if (StringUtils.isNotEmpty(sql)) {
            sql.insert(0, " where ");
        }
        return sql.toString();
    }

    public String delete(Q q) {
        if (q == null) {
            throw new ServiceException("参数不能为空");
        }
        String tableName = this.getTableNameByQO(q.getClass());
        StringBuilder sql = new StringBuilder("delete from "+tableName);
        List<Field> qoFields = Arrays.stream(ReflectUtil.getFields(q.getClass())).filter(qoField->ObjectUtil.isNotEmpty(ReflectUtil.getFieldValue(q, qoField))).filter(qoField->qoField.getAnnotation(Transient.class)==null).collect(Collectors.toList());
        if (!qoFields.isEmpty()) {

//            sql.append(" where ");
//            sql.append(StringUtils.join(qoFields.stream().map(qoField -> StringUtils.camelToSnake(qoField.getName()) + " = #{" + qoField.getName() + "}").collect(Collectors.toList()), " and "));
            sql.append(this.getWhere(q, qoFields));
        }else{
            throw new ServiceException("删除对象至少要一个条件");
        }
        return sql.toString();
    }
}
